package edu.cicese.tm;

import com.sun.java.swing.plaf.windows.WindowsBorders;
import edu.cicese.graphics.DashBorder;
import edu.cicese.graphics.JMultiLineToolTip;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JToolTip;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by: Eduardo Quintana Contreras
 * Date: 1/03/11
 * Time: 10:13 AM
 */
public class FrameSpace extends JFrame {

	private JFrame frmMain;
	private FrameSpace frmSpace;
	private FrameTagCreation frmTagCreation;
	private FrameTagEdition frmTagEdition;

	public JPanel pThumbnails;
	private JScrollPane lowerScrollPanel, upperScrollPanel;
	private JPanel pTags;
	private JSplitPane upperSplitPane;
	private JLabel lblImgPreview, lblTagPreview;
	private JTextArea taTagAnnotation;
	private JButton[] imgButtons;
	private JLabel lblTagInfo1, lblTagInfo2, lblImageInfo1, lblImageInfo2;

	public String spaceID;
	private String thumbDirectory, previewDirectory, tagDirectory;
	private Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();

	private final int tagPreviewWidth = 173, tagPreviewHeight = 130, tagThumbWidth = 80, tagThumbHeight = 60;
	private JPanel pTagTitle;
	private BufferedImage cachedTitleBackground;
	public List<Thumb> imgThumbnails = new ArrayList<Thumb>();
	private List<Thumb> tagThumbnails = new ArrayList<Thumb>();
	public static Thumb selectedImageThumb, selectedTagThumb;

	private String imageLoadingDir;

	public FrameSpace(JFrame father) {
		frmMain = father;
		frmSpace = this;
		setSize(910, 562);
		setResizable(false);
		setLayout(null);
		setIconImage(new ImageIcon(getClass().getClassLoader().getResource("icons/icon.png")).getImage());

		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				closeWindow();
			}
		});
		initComponents();
	}

	public void setValues(String spaceID, String spaceName) {
		this.spaceID = spaceID;
		tagDirectory = Utilities.getTagDir(spaceID) + Utilities.tagSize + "\\";
//		previewDirectory = Utilities.getImgDir(spaceID) + "320x240\\";
		previewDirectory = Utilities.getImgDir(spaceID) + "480x360\\";
		thumbDirectory = Utilities.getImgDir(spaceID) + "100x75\\";
		setTitle(spaceName);
		setComponents();
	}

	//! Creates the GUI components
	private void initComponents() {
		// UPPER PANEL
		// Tags
		pTags = new JPanel();
		pTags.setLayout(null);

		upperScrollPanel = new JScrollPane(pTags, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		upperScrollPanel.setBorder(BorderFactory.createEmptyBorder());
		upperScrollPanel.getVerticalScrollBar().setUnitIncrement(16);

		JPanel pUpperRight = new JPanel();
		pUpperRight.setLayout(null);

		lblTagPreview = new JLabel("", JLabel.CENTER);
		lblTagPreview.setBounds(33, 2, tagPreviewWidth, tagPreviewHeight);
		lblTagPreview.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2 && !e.isConsumed()) {
					e.consume();
					openTagEditionFrame();
				}
			}
		});

		taTagAnnotation = new JTextArea();
//		taTagAnnotation.setFont(new Font("Segoe UI", Font.PLAIN, 13));
		taTagAnnotation.setLineWrap(true);
		taTagAnnotation.setForeground(Color.darkGray);
		taTagAnnotation.setEditable(false);

		JScrollPane spTagAnnotation = new JScrollPane(taTagAnnotation, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		spTagAnnotation.setBounds(5, 134, 268, 60);
		spTagAnnotation.setBorder(new WindowsBorders.DashedBorder(Color.LIGHT_GRAY, 1));

		JButton btnEdit = new JButton();
		addButton(btnEdit, 239, 1, 32, 32, "Editar etiqueta", "edit", pUpperRight, null);
		btnEdit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionEditTag();
			}
		});

		JButton btnDelTag = new JButton();
		addButton(btnDelTag, 239, 34, 32, 32, "Eliminar etiqueta", "delTag", pUpperRight, null);
		btnDelTag.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionDeleteTag();
			}
		});

		JButton btnAudioTag = new JButton();
		addButton(btnAudioTag, 239, 67, 32, 32, "Reproducir audio", "audioTag", pUpperRight, null);
		btnAudioTag.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionAudioTag();
			}
		});
		
		JButton btnVideoTag = new JButton();
		addButton(btnVideoTag, 239, 100, 32, 32, "Reproducir video", "videoTag", pUpperRight, null);
		btnVideoTag.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionVideoTag();
			}
		});

		pUpperRight.add(lblTagPreview);
		pUpperRight.add(spTagAnnotation);

		upperSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		upperSplitPane.setDividerSize(0);
		upperSplitPane.setDividerLocation(617);
		upperSplitPane.setLeftComponent(upperScrollPanel);
		upperSplitPane.setRightComponent(pUpperRight);
		upperSplitPane.setBounds(0, 50, 958, 195);
		upperSplitPane.setBorder(new EmptyBorder(0, 0, 0, 0));

		// LOWER PANEL
		// Thumbnails
		pThumbnails = new JPanel();
		pThumbnails.setLayout(null);
		pThumbnails.setFocusable(true);
	/*	pThumbnails.addKeyListener(new KeyListener() {
			public void keyTyped(KeyEvent e) {

				if(e.getKeyCode() == KeyEvent.VK_DELETE) {
					System.out.println("SUPRM");
				}
			}

			public void keyPressed(KeyEvent e) {
				System.out.println("Pressed " + e.getKeyChar());
				//To change body of implemented methods use File | Settings | File Templates.
			}

			public void keyReleased(KeyEvent e) {
				System.out.println("Released " + e.getKeyChar());
				if(e.getKeyCode() == KeyEvent.VK_DELETE) {
					System.out.println("SUPRM");
				}
				//To change body of implemented methods use File | Settings | File Templates.
			}
		});*/

		lowerScrollPanel = new JScrollPane(pThumbnails, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		lowerScrollPanel.setBorder(BorderFactory.createEmptyBorder());
		lowerScrollPanel.getVerticalScrollBar().setUnitIncrement(16);

		// Preview
		JPanel pLowerRight = new JPanel();
		pLowerRight.setLayout(null);

		lblImgPreview = new JLabel();
		lblImgPreview.setSize(300, 225);
		lblImgPreview.setLocation(5, 5);
		lblImgPreview.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2 && !e.isConsumed()) {
					e.consume();
					openTagCreationFrame();
				}
			}
		});

		JButton btnAdd = new JButton();
		addButton(btnAdd, 310, 5, 32, 32, "Agregar imagen", "addImg", pLowerRight, null);
		btnAdd.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionAddImages();
			}
		});

		JButton btnDelete = new JButton();
		addButton(btnDelete, 310, 42, 32, 32, "Eliminar imagen", "delImg", pLowerRight, null);
		btnDelete.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionDeleteImage();
			}
		});

		JButton btnDeleteAll = new JButton();
		addButton(btnDeleteAll, 310, 79, 32, 32, "Eliminar todas las imágenes", "delAllImg", pLowerRight, null);
		btnDeleteAll.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionDeleteAllImages();
			}
		});

		JButton btnSave = new JButton();
		addButton(btnSave, 310, 116, 32, 32, "Guardar cambios", "save", pLowerRight, null);
		btnSave.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionSaveChanges();
			}
		});

		JButton btnCancel = new JButton();
		addButton(btnCancel, 310, 153, 32, 32, "Cancelar cambios", "cancel", pLowerRight, null);
		btnCancel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionCancelChanges();
			}
		});

		imgButtons = new JButton[] { btnDelete, btnDeleteAll, btnSave, btnCancel };
		pLowerRight.add(lblImgPreview);

		JSplitPane lowerSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		lowerSplitPane.setDividerSize(0);
		lowerSplitPane.setDividerLocation(547);
		lowerSplitPane.setLeftComponent(lowerScrollPanel);
		lowerSplitPane.setRightComponent(pLowerRight);
		lowerSplitPane.setBounds(0, 296, 958, 237);
		lowerSplitPane.setBorder(new EmptyBorder(0, 0, 0, 0));

		JLabel lblTags = new JLabel("ETIQUETAS"), lblImgs = new JLabel("IMÁGENES");
		lblTags.setFont(new Font("Century Gothic", Font.BOLD, 40));
		lblTags.setForeground(Color.WHITE);
		lblTags.setBounds(5, -5, 200, 55);
		lblImgs.setFont(new Font("Century Gothic", Font.BOLD, 18));
		lblImgs.setBounds(5, 270, 200, 30);

		pTagTitle = new JPanel(){
			public void paintComponent(Graphics g) {
				super.paintComponent(g);
				g.drawImage(cachedTitleBackground, 0, 0, null);
			}
		};
		pTagTitle.setBounds(0, 0, 960, 46);
		pTagTitle.setLayout(null);
		pTagTitle.add(lblTags);
		cachedTitleBackground = (BufferedImage)createImage(960, 46);

		JButton btnBack = new JButton();
		addButton(btnBack, 819, 5, 32, 32, "Regresar", "back", pTagTitle, null);
		btnBack.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionBack();
			}
		});

		JButton btnCopy = new JButton();
		addButton(btnCopy, 856, 5, 32, 32, "Crear etiqueta con datos existentes", "copy", pTagTitle, null);
		btnCopy.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionCopy();
			}
		});

		getContentPane().add(pTagTitle);
		getContentPane().add(lblImgs);
		getContentPane().add(upperSplitPane);
		getContentPane().add(lowerSplitPane);

		lblTagInfo1 = new JLabel("", SwingConstants.CENTER);
		SwingUtil.addLabel(lblTagInfo1, 0, 155, 910, 20, "No hay etiquetas para mostrar",
				null, getContentPane());
		lblTagInfo1.setHorizontalTextPosition(SwingConstants.CENTER);
		lblTagInfo1.setFont(new Font("Segoe UI", Font.BOLD, 15));
		lblTagInfo1.setForeground(Color.LIGHT_GRAY);
		lblTagInfo2 = new JLabel("", SwingConstants.CENTER);
		SwingUtil.addLabel(lblTagInfo2, 0, 175, 910, 20,
				"Para agregar una etiqueta haga doble clic sobre una imagen de la sección inferior",
				null, getContentPane());
		lblTagInfo2.setHorizontalTextPosition(SwingConstants.CENTER);
		lblTagInfo2.setFont(new Font("Segoe UI", Font.BOLD, 15));
		lblTagInfo2.setForeground(Color.LIGHT_GRAY);

		lblImageInfo1 = new JLabel("", SwingConstants.CENTER);
		//0, 296, 958, 237
		SwingUtil.addLabel(lblImageInfo1, 0, 370, 910, 20, "No hay imágenes asociadas a este espacio",
				null, getContentPane());
		lblImageInfo1.setHorizontalTextPosition(SwingConstants.CENTER);
		lblImageInfo1.setFont(new Font("Segoe UI", Font.BOLD, 15));
		lblImageInfo1.setForeground(Color.LIGHT_GRAY);
		lblImageInfo2 = new JLabel("", SwingConstants.CENTER);
		SwingUtil.addLabel(lblImageInfo2, 0, 390, 910, 20, "Para agregar una imagen utilice el botón de la derecha",
				null, getContentPane());
		lblImageInfo2.setHorizontalTextPosition(SwingConstants.CENTER);
		lblImageInfo2.setFont(new Font("Segoe UI", Font.BOLD, 15));
		lblImageInfo2.setForeground(Color.LIGHT_GRAY);

		getContentPane().setComponentZOrder(lblImageInfo1, 0);
		getContentPane().setComponentZOrder(lblImageInfo2, 0);
	}

	//! Sets the tag and image thumbnail images
	@SuppressWarnings({"ConstantConditions"})
	public void setComponents() {
		// TAGS
		pTags.removeAll();
		tagThumbnails.clear();

		File dir = new File(tagDirectory);
		FileFilter filter = new FileFilter() {
			public boolean accept(File file) {
				return file.isDirectory();
			}
		};

		File[] files = dir.listFiles(filter);

		// Populate with tags
		int posY, posX, yAddition, xAddition, mod;
		if (files != null) {
			upperSplitPane.setVisible(true);
			
			xAddition = tagThumbWidth + 5;
			yAddition = tagThumbHeight + 17;
			posX = 5;
			posY = -76;

			for (int i = 0; i < files.length; i++, posX += xAddition) {
				try {
					final String tagID = files[i].getName(), toolTip;
					final String filename = files[i].getCanonicalPath() + "\\" + tagID;
					String annotation = new File(filename + ".txt").exists() ?
							Utilities.loadString(filename + ".txt") : null;
					final String tagAnnotation = annotation == null || !annotation.equals("") ? annotation : null;
					String date = new File(filename + ".date").exists() ? Utilities.loadString(filename + ".date") : "";
					final boolean enabled;
					JSONObject joDate = (JSONObject) JSONValue.parse(date);
					if (joDate != null && joDate.get("enabled") != null) {
						enabled = Boolean.valueOf(joDate.get("enabled").toString());
					}
					else {
						enabled = true;
					}
					final String iconName;
					if (enabled) {
						iconName = "available";
						toolTip = "Habilitada";
					}
					else {
						iconName = "unavailable";
						toolTip = "Deshabilitada";
					}

					mod = i % 7;
					if (mod == 0) {
						posX = 5;
						posY += yAddition;
					}

					JLabel label = new JLabel(new ImageIcon(SwingUtil.getScaledImage(
							new ImageIcon(filename + ".png").getImage(), tagThumbWidth, tagThumbHeight))) {
						public JToolTip createToolTip() {
							return new JMultiLineToolTip();
						}
					};
					JLabel lblEnabled = new JLabel(new ImageIcon(
							getClass().getClassLoader().getResource("icons/" + iconName + ".png")));
					lblEnabled.setToolTipText(toolTip);
					final Thumb tagThumb = new Thumb(label, lblEnabled);
					tagThumbnails.add(tagThumb);
					tagThumb.tagID = tagID;
					SwingUtil.addLabel(tagThumb.label, posX, posY, tagThumbWidth, tagThumbHeight, "", pTags, null);
					SwingUtil.addLabel(tagThumb.lblEnabled, posX + tagThumbWidth - 12,
							posY + tagThumbHeight + 1, 12, 12, "", pTags, null);
					tagThumb.label.setBorder(new WindowsBorders.DashedBorder(Color.LIGHT_GRAY, 1));
					tagThumb.label.setToolTipText(tagAnnotation);
					tagThumb.label.addMouseListener(new MouseAdapter() {
						public void mouseClicked(MouseEvent e) {
							if (e.getClickCount() == 2 && !e.isConsumed()) {
								e.consume();
								openTagEditionFrame(tagID);
							}
						}
						public void mousePressed(MouseEvent e) {
							tagThumb.label.requestFocusInWindow();
							setSelectedTagThumb(tagThumb);
							paintBorder(tagThumb, true);
							showTagPreview(filename, tagAnnotation);
						}
						public void mouseEntered(MouseEvent e) {
							paintBorder(tagThumb, true);
						}
						public void mouseExited(MouseEvent e) {
							paintBorder(tagThumb, false);
						}
					});
					tagThumb.label.addKeyListener(new KeyListener() {
						public void keyTyped(KeyEvent e) {
						}
						public void keyPressed(KeyEvent e) {
							switch (e.getKeyCode()) {
								case KeyEvent.VK_UP:
									setSelectedTagThumb(-7);
									break;
								case KeyEvent.VK_DOWN:
									setSelectedTagThumb(7);
									break;
								case KeyEvent.VK_RIGHT:
									setSelectedTagThumb(1);
									break;
								case KeyEvent.VK_LEFT:
									setSelectedTagThumb(-1);
									break;
							}
						}
						public void keyReleased(KeyEvent e) {
							switch (e.getKeyCode()) {
								case KeyEvent.VK_DELETE:
									if (e.isShiftDown()) {
										actionDeleteTag();
									}
									else {
										enableTag(false, filename);
										tagThumb.lblEnabled.setIcon(new ImageIcon(
												getClass().getClassLoader().getResource("icons/unavailable.png")));
										tagThumb.lblEnabled.setToolTipText("Deshabilitada");
									}
									break;
								case KeyEvent.VK_ENTER:
									openTagEditionFrame(tagID);
									break;
								case KeyEvent.VK_INSERT:
									enableTag(true, filename);
									tagThumb.lblEnabled.setIcon(new ImageIcon(
											getClass().getClassLoader().getResource("icons/available.png")));
									tagThumb.lblEnabled.setToolTipText("Habilitada");
									break;
							}
						}
					});
				} catch (Exception e) { e.printStackTrace(); }
			}

			pTags.setPreferredSize(new Dimension(600, ((int) Math.ceil(files.length / 7.0) * (tagThumbHeight + 17)) + 1));
			//pTh.setPreferredSize(new Dimension(530, ((int) Math.ceil(imgThumbnails.size() / 5.0) * 80) + 1));
			pTags.repaint();
			pTags.revalidate();
		}
		if (!tagThumbnails.isEmpty()) {
			try {
				String filename = files[0].getCanonicalPath() + "\\" + files[0].getName();
				String annotation = new File(filename + ".txt").exists() ?
						Utilities.loadString(filename + ".txt") : null;
				String tagAnnotation = annotation == null || !annotation.equals("") ? annotation : null;
//				String tagAnnotation = new File(filename + ".txt").exists() ?
//							Utilities.loadString(filename + ".txt") : "";
				selectedTagThumb = tagThumbnails.get(0);
				setSelectedTagThumb(tagThumbnails.get(0));
				paintBorder(tagThumbnails.get(0), true);
				showTagPreview(filename, tagAnnotation);
			} catch (IOException e) { e.printStackTrace(); }
		}
		else {
			selectedTagThumb = null;
			lblTagPreview.setIcon(null);
			upperSplitPane.setVisible(false);
		}

		// THUMBNAILS
		pThumbnails.removeAll();
		imgThumbnails.clear();

		dir = new File(thumbDirectory);
		filter = new FileFilter() {
      		public boolean accept(File file) {
          		return file.getName().toLowerCase().endsWith(".jpg");
      		}
		};
		files = dir.listFiles(filter);
		posY = -79;
		posX = 5;

		// Populate with thumbnails
		if (files != null) {
			int sep;

			for (int i = 0; i < files.length; i++, posX += 105) {
				try {
					final String imagePath = files[i].getCanonicalPath();
					sep = imagePath.lastIndexOf(File.separator);
					final String imageName = imagePath.substring(sep + 1, imagePath.length());
					mod = i % 5;
					if (mod == 0) {
						posX = 5;
						posY += 80;
					}
					final Thumb thumb = new Thumb(new JLabel());
					addLabel(thumb, pThumbnails, imgThumbnails, posX, posY, 100, 75, imagePath, imageName, true);
					thumb.label.addMouseListener(new MouseAdapter() {
						public void mouseClicked(MouseEvent e) {
							if (e.getClickCount() == 2 && !e.isConsumed()) {
								e.consume();
								openTagCreationFrame(imageName);
							}
						}
						public void mousePressed(MouseEvent e) {
							thumb.label.requestFocusInWindow();
							setSelectedImageThumb(thumb);
							paintBorder(thumb, true);
							showImagePreview();
						}
						public void mouseEntered(MouseEvent e) {
							paintBorder(thumb, true);
						}
						public void mouseExited(MouseEvent e) {
							paintBorder(thumb, false);
						}
					});
					thumb.label.addKeyListener(new KeyListener() {
						public void keyTyped(KeyEvent e) {
						}
						public void keyPressed(KeyEvent e) {
							switch (e.getKeyCode()) {
								case KeyEvent.VK_UP:
									setSelectedImageThumb(-5);
									break;
								case KeyEvent.VK_DOWN:
									setSelectedImageThumb(5);
									break;
								case KeyEvent.VK_RIGHT:
									setSelectedImageThumb(1);
									break;
								case KeyEvent.VK_LEFT:
									setSelectedImageThumb(-1);
									break;
							}
						}
						public void keyReleased(KeyEvent e) {
							switch (e.getKeyCode()) {
								case KeyEvent.VK_DELETE:
									actionDeleteImage();
									break;
								case KeyEvent.VK_ENTER:
									openTagCreationFrame(imageName);
									break;
							}
						}
					});
				} catch (Exception e) { e.printStackTrace(); }
			}
		}
		if (imgThumbnails.size() > 0) {
			selectedImageThumb = imgThumbnails.get(0);
			setSelectedImageThumb(imgThumbnails.get(0));
			paintBorder(imgThumbnails.get(0), true);
			showImagePreview();
			pThumbnails.setPreferredSize(new Dimension(530, ((int) Math.ceil(imgThumbnails.size() / 5.0) * 80) + 1));
			showImageButtons(true);
		}
		else {
			selectedImageThumb = null;
			lblImgPreview.setIcon(null);
			pThumbnails.setPreferredSize(new Dimension(530, 0));
			showImageButtons(false);
		}
		pThumbnails.repaint();
		pThumbnails.revalidate();

		addInfo();
	}

	/**
	 * Shows or hides help information about the tag creation and image addition.
	 */
	public void addInfo() {
		// show info in the tag pane
		lblTagInfo1.setVisible(tagThumbnails.isEmpty());
		lblTagInfo2.setVisible(tagThumbnails.isEmpty());
		// show info in the image pane
		lblImageInfo1.setVisible(imgThumbnails.isEmpty());
		lblImageInfo2.setVisible(imgThumbnails.isEmpty());
	}

	/**
	 * Shows or hides the image pane buttons (except for the {@code 'add'} button).
	 *
	 * @param state {@code true} to show the buttons, {@code false} otherwise
	 */
	public void showImageButtons(boolean state) {
		for (JButton btn : imgButtons) {
			btn.setVisible(state);
		}
	}

	/**
	 * Closes the window and brings back the {@link FrameSpaceManagement} window.
	 */
	private void closeWindow() {
		frmMain.setVisible(true);
		frmMain.toFront();
	}

	/**
	 * Opens the {@link FrameTagCreation} window if an image thumbnail is currently
	 * selected and it hasn't been added recently.
	 */
	private void openTagCreationFrame() {
		if (selectedImageThumb != null && !selectedImageThumb.isNew) {
			openTagCreationFrame(selectedImageThumb.imageName);
		}
	}

	/**
	 * Opens the {@link FrameTagCreation} window of the image {@code imgID}.
	 *
	 * @param imgID the identifier of the image to be used to create
	 * a new tag.
	 */
	private void openTagCreationFrame(String imgID) {
		DialogAudio.tmpAudioSaved = false;
		// remove the extension
		imgID = imgID.substring(0, 3);
		setVisible(false);
		// create or open the tag creation frame
		if (frmTagCreation == null) {
			frmTagCreation = new FrameTagCreation(this);
		}
		frmTagCreation.setSize(565, 451);
		frmTagCreation.setValues(spaceID);
		Rectangle window = frmTagCreation.getBounds();
		frmTagCreation.setLocation((screen.width - window.width) / 2, (screen.height - window.height) / 2);
		frmTagCreation.setVisible(true);
		frmTagCreation.drawImageQueried(imgID);
		frmTagCreation.mainFrameResized = false;
	}

	/**
	 * Opens the {@link FrameTagCreation} window if an image thumbnail is currently
	 * selected and it hasn't been added recently.
	 */
	private void openTagCreationFrameWithInfo() {
		// TODO: Show error dialogs
		if(selectedTagThumb == null) {
			System.out.println("Seleccionar etiqueta por favor..");
		}
		else if (selectedImageThumb == null || selectedImageThumb.isNew) {
			System.out.println("Seleccionar imagen (previamente procesada) por favor..");
		}
		else {
			openTagCreationFrameWithInfo(selectedTagThumb.tagID, selectedImageThumb.imageName);
		}
	}

	/**
	 * Opens the {@link FrameTagCreation} window of the image {@code imgID}.
	 *
	 * @param tagID the...
	 * @param imgID the identifier of the image to be used to create
	 * a new tag.
	 */
	private void openTagCreationFrameWithInfo(String tagID, String imgID) {
		DialogAudio.tmpAudioSaved = false;
		// remove the extension
		imgID = imgID.substring(0, 3);
		setVisible(false);
		// create or open the tag creation frame
		if (frmTagCreation == null) {
			frmTagCreation = new FrameTagCreation(this);
		}
		frmTagCreation.setSize(565, 451);
		frmTagCreation.setValues(spaceID, tagID);
		Rectangle window = frmTagCreation.getBounds();
		frmTagCreation.setLocation((screen.width - window.width) / 2, (screen.height - window.height) / 2);
		frmTagCreation.setVisible(true);
		frmTagCreation.drawImageQueried(imgID);
		frmTagCreation.mainFrameResized = false;
	}

	/**
	 * Opens the {@link FrameTagEdition} window if a tag thumbnail is currently selected.
	 */
	private void openTagEditionFrame() {
		if (selectedTagThumb != null) {
			openTagEditionFrame(selectedTagThumb.tagID);
		}
	}

	/**
	 * Opens the {@link FrameTagEdition} window of the tag {@code tagID}.
	 *
	 * @param tagID the identifier of the tag to be edited.
	 */
	private void openTagEditionFrame(String tagID) {
		DialogAudio.tmpAudioSaved = false;
		setVisible(false);
		// create or open the tag edition frame
		if (frmTagEdition == null) {
			frmTagEdition = new FrameTagEdition(this);
		}
		frmTagEdition.setValues(spaceID, tagID);
		Rectangle window = frmTagEdition.getBounds();
		frmTagEdition.setLocation((screen.width - window.width) / 2, (screen.height - window.height) / 2);
		frmTagEdition.setVisible(true);
	}

	/**
	 * Shows the currently selected image preview.
	 */
	public void showImagePreview() {
		String imagePath;
		if (!selectedImageThumb.isNew) {
			imagePath = previewDirectory + selectedImageThumb.imageName;
		}
		else {
			imagePath = selectedImageThumb.imagePath;
		}
		lblImgPreview.setIcon(new ImageIcon(
				new ImageIcon(imagePath).getImage().getScaledInstance(300, 225, Image.SCALE_FAST)));
	}

	/**
	 * Shows the currently selected tag preview and textual annotation.
	 *
	 * @param filename the file path of the tag image to be previewed
	 * @param annotation the textual annotation to be previewed
	 */
	private void showTagPreview(String filename, String annotation) {
		// preview tag image
		lblTagPreview.setIcon(new ImageIcon(SwingUtil.getScaledImage(new ImageIcon(filename + ".png").getImage(),
				tagPreviewWidth, tagPreviewHeight)));
		// preview annotation
		if (annotation != null) {
			taTagAnnotation.setFont(new Font("Segoe UI", Font.PLAIN, 13));
			taTagAnnotation.setText(annotation);
		}
		else {
			taTagAnnotation.setFont(new Font("Segoe UI", Font.ITALIC, 13));
			taTagAnnotation.setText("Anotación vacía.");
		}
		taTagAnnotation.setCaretPosition(0);
	}

	/**
	 * Called when the {@code 'back'} button is clicked.
	 */
	private void actionBack() {
		setVisible(false);
		closeWindow();
	}

	/**
	 * Called when the {@code 'copy tag'} button is clicked.
	 * Creates a tag with existent info.
	 */
	private void actionCopy() {
		openTagCreationFrameWithInfo();
	}

	/**
	 * Called when the {@code 'edit tag'} button is clicked.
	 */
	private void actionEditTag() {
		openTagEditionFrame();
	}

	/**
	 * Called when the {@code 'delete tag'} button is clicked. Shows a
	 * confirmation dialog to delete the tag permanently.
	 */
	private void actionDeleteTag() {
		if (selectedTagThumb != null) {
			// show confirmation dialog
			if (JOptionPane.showConfirmDialog(this, "¿Desea eliminar la etiqueta de forma permanente?",
					"Eliminar etiqueta", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
					new ImageIcon(getClass().getClassLoader().getResource("icons/warning.png")))
					== JOptionPane.YES_OPTION) {
				// delete the selected tag's directories
				//TODO: Remove? Leave only ONE tag size?
				for (String tagSize : Utilities.tagSizes) {
					Utilities.deleteDirectory(Utilities.getTagDir(spaceID) + tagSize + "\\" + selectedTagThumb.tagID);
				}
				// add a 'tag deletion' entry to the tag changelog
				deleteTagChangelog(selectedTagThumb.tagID);
				// update the window's components
				setComponents();
			}
		}
	}

	/**
	 * Adds a 'tag deletion' entry to the tag changelog.
	 *
	 * @param tagID the identifier of the tag to be deleted
	 */
	@SuppressWarnings({"unchecked"})
	private void deleteTagChangelog(String tagID) {
		// load the tag changelog
		JSONArray jaChangelog = Utilities.loadJSONArray(Utilities.spaceDir + "changelog.ans");
		JSONObject joSpace = new JSONObject();
		JSONArray jaTags = new JSONArray();
		JSONObject joTag = new JSONObject();
		boolean spaceFound = false;
		// loop through the space entries and search for the current space
		for (Object obj : jaChangelog) {
			joSpace = (JSONObject) obj;
			if (joSpace.get("spaceID").toString().equals(spaceID) &&
					Integer.parseInt(joSpace.get("action").toString()) != Utilities.DELETED) {
				spaceFound = true;
				break;
			}
		}
		// if there isn't a space entry for the current space
		if (!spaceFound) {
			// create the space entry
			joSpace = new JSONObject();
			joSpace.put("spaceID", spaceID);
			joSpace.put("action", Utilities.NO_ACTION);
			// create the 'tag deletion' entry
			joTag = new JSONObject();
			joTag.put("tagID", tagID);
			joTag.put("action", Utilities.DELETED);
			jaTags.add(joTag);
			joSpace.put("tags", jaTags);
			jaChangelog.add(joSpace);
		}
		// if there is a space entry for the current space
		else {
			// load the tag entries
			jaTags = (JSONArray) joSpace.get("tags");
			boolean tagFound = false;
			// loop through the tag entries and search for tag selected
			for (Object obj : jaTags) {
				joTag = (JSONObject) obj;
				if (joTag.get("tagID").toString().equals(tagID) &&
						Integer.parseInt(joTag.get("action").toString()) != Utilities.DELETED) {
					tagFound = true;
					break;
				}
			}
			// if there isn't a tag entry for the selected tag
			if (!tagFound) {
				// create the 'tag deletion' entry
				joTag = new JSONObject();
				joTag.put("tagID", tagID);
				joTag.put("action", Utilities.DELETED);
				jaTags.add(joTag);
			}
			// if there is a tag entry for the selected tag
			else {
				// if the entry is a 'tag modified' entry, change it to 'deletion entry'
				if (Integer.parseInt(joTag.get("action").toString()) == Utilities.MODIFIED) {
					joTag.put("action", Utilities.DELETED);
				}
				// if the entry is a 'tag creation' entry, remove it
				else if (Integer.parseInt(joTag.get("action").toString()) == Utilities.CREATED) {
					jaTags.remove(joTag);
				}
			}
		}
		// save to the changelog file used to update the server tag repository cache
		Utilities.addToLog(frmSpace.spaceID, tagID, "TAGchangelog.ans");
		// save to the changelog file used to update the client tag repository
		Utilities.saveString(Utilities.spaceDir + "changelog.ans", jaChangelog.toJSONString());
	}

	/**
	 * Called when the {@code 'audio tag'} button is clicked.
	 */
	private void actionAudioTag() {
	}

	/**
	 * Called when the {@code 'video tag'} button is clicked.
	 */
	private void actionVideoTag() {
	}

	/**
	 * Called when the {@code 'add images'} button is clicked.
	 * Shows a {@link JFileChooser} to select images to add to the current space.
	 */
	private void actionAddImages() {
		// open file chooser to select images to add
		JFileChooser fcImgQueried = new JFileChooser();
		PreviewPane previewPane = new PreviewPane();
		fcImgQueried.setAccessory(previewPane);
		fcImgQueried.addPropertyChangeListener(previewPane);
		// filter images by extension (.jpg)
		FileNameExtensionFilter extensionFilter = new FileNameExtensionFilter("Imágenes .JPG", "jpg");
		fcImgQueried.setFileFilter(extensionFilter);
		fcImgQueried.setFileSelectionMode(JFileChooser.FILES_ONLY);
		fcImgQueried.setMultiSelectionEnabled(true);
		if (imageLoadingDir != null) {
			fcImgQueried.setCurrentDirectory(new File(imageLoadingDir));
		}
		if (fcImgQueried.showDialog(this, "Abrir") == JFileChooser.APPROVE_OPTION) {
			// create a dialog to show the progress of the image thumbnails creation
			new Thread(new DialogLoadingImages(frmSpace, fcImgQueried.getSelectedFiles())).start();
			try {
				imageLoadingDir = fcImgQueried.getCurrentDirectory().getCanonicalPath();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Marks the currently selected image as 'to-delete'.
	 */
	public void actionDeleteImage() {
		if (selectedImageThumb != null) {
			selectedImageThumb.toDelete = true;
			// change the thumbnail border
			selectedImageThumb.label.setBorder(new DashBorder(Color.BLACK, selectedImageThumb, false));
		}
	}

	/**
	 * Marks all images in the current space as 'to-delete'.
	 */
	private void actionDeleteAllImages() {
		for (Thumb thumb : imgThumbnails) {
			// change the border
			thumb.label.setBorder(new DashBorder(Color.BLACK, thumb, false));
			thumb.toDelete = true;
		}
	}

	/**
	 * Called when the {@code 'save changes'} button is clicked. Shows a
	 * confirmation dialog to save the changes permanently.
	 */
	private void actionSaveChanges() {
		// show confirmation dialog
		if (imgThumbnails.size() > 0 &&
				JOptionPane.showConfirmDialog(this, "¿Desea guardar los cambios de forma permanente?",
				"Guardar cambios", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
				new ImageIcon(getClass().getClassLoader().getResource("icons/warning.png")))
				== JOptionPane.YES_OPTION) {
			// create a dialog to show the progress of the image creation and/or deletion
			new Thread(new DialogChanges(frmSpace)).start();
		}
	}

	/**
	 * Called when the {@code 'cancel changes'} button is clicked.
	 * Cancels all the changes made in the current space.
	 */
	private void actionCancelChanges() {
		List<Integer> thumbIndexes = new ArrayList<Integer>();
		int i = 0;
		for (Thumb thumb : frmSpace.imgThumbnails) {
			// if the image entry already existed
			if (!thumb.isNew) {
				thumb.toDelete = false;
			}
			// if the image entry didn't exist
			else {
				thumbIndexes.add(i);
			}
			i++;
		}
		// remove image thumbnails
		for (i = 0; !thumbIndexes.isEmpty(); i++) {
			frmSpace.imgThumbnails.remove(thumbIndexes.get(0) - i);
			thumbIndexes.remove(0);
		}
		// update the window's components
		setComponents();
	}

	//! Gets a new image ID
	@SuppressWarnings({"ConstantConditions"})
	public String getNewImageID(List<String> imageIDs) {
		int nextIndex = 1, fileIndex;
		String imageID;
		File[] files = new File(Utilities.getPoiDir(spaceID)).listFiles();
		if (files != null) {
			for (File file : files) {
				try {
					fileIndex = Integer.parseInt(file.getName().substring(0, 3));
					if (nextIndex < fileIndex) {
						imageID = Utilities.addZeros(nextIndex, 3);
						if (!isTaken(imageID, imageIDs)) {
							return imageID;
						}
					}
					nextIndex++;
				} catch (Exception e) { System.out.println("Alguien estuvo jugando con los nombres de las imágenes."); }
			}
		}
		do {
			imageID = Utilities.addZeros(nextIndex, 3);
			nextIndex++;
		} while (isTaken(imageID, imageIDs));
		imageIDs.add(imageID);
		return imageID;
	}

	//! Checks an image ID availability
	private boolean isTaken(String index, List<String> imageIDs) {
		for (String id : imageIDs) {
			if (id.equals(index)) {
				return true;
			}
		}
		return false;
	}

	//! Adds a JButton
	private void addButton(JButton button, int x, int y, int width, int height,
	                              String toolTipText, String iconName, JPanel pane, Container container) {
		Icon icon = new ImageIcon(getClass().getClassLoader().getResource("icons/" + iconName + ".png"));
		Icon rIcon = new ImageIcon(getClass().getClassLoader().getResource("icons/" + iconName + "O.png"));
		Icon pIcon = new ImageIcon(getClass().getClassLoader().getResource("icons/" + iconName + "P.png"));
		SwingUtil.addButton(button, x, y, width, height, toolTipText, icon, rIcon, pIcon, pane, container);
	}

	//! Adds a JLabel
	public void addLabel(Thumb thumb, JPanel pane, List<Thumb> list, int x, int y,
	                     int width, int height, String imagePath, String imageName, boolean thumbExists) {
		pane.add(thumb.label);
		thumb.label.setBorder(new EmptyBorder(0, 1, 0, 0));

		//  Using ImageIO
/*
		String imageName = "timeLabel.jpg";
		imageLabel.setIcon( new ImageIcon(ImageIO.read( new File(imageName) ) ) );

		//  Or you can flush the image

		String imageName = "timeLabel.jpg";
		ImageIcon icon = new ImageIcon(imageName);
		icon.getImage().flush();
		imageLabel.setIconimageLabel.setIcon( icon );*/

		if (thumbExists) {
//			thumb.label.setIcon(new ImageIcon(new ImageIcon(Utilities.emptyThumb).getImage()));
//			thumb.label.repaint();
			ImageIcon icon = new ImageIcon(imagePath);
			icon.getImage().flush();
			thumb.label.setIcon(icon);
//			thumb.label.setIcon(new ImageIcon(new ImageIcon(imagePath).getImage()));
//			thumb.label.repaint();
		}
		else {
			//TODO: Remove getScaledInstance
			BufferedImage img;
			try {
//				File imgFile = new File(imagePath);
//				if (imgFile.exists()){
					img = ImageIO.read(new File(imagePath));
					thumb.label.setIcon(new ImageIcon(getScaledInstance(img, width, height, false)));
//				}
			} catch (IOException e) {
				e.printStackTrace();
			}


//			thumb.label.setIcon(new ImageIcon(SwingUtil.getScaledImage(new ImageIcon(imagePath).getImage(),
//							width, height)));
			thumb.label.setBorder(new DashBorder(Color.BLACK, thumb, false));
		}
		thumb.label.setBounds(x, y, width, height);
		thumb.label.setToolTipText(imageName);
		thumb.imageName = imageName;
		thumb.imagePath = imagePath;
		list.add(thumb);
	}

	/**
	 * Selects a tag thumbnail, deselecting the previously selected tag thumbnail and
	 * updating the thumbnail borders.
	 *
	 * @param thumb the {@code Thumb} to select
	 */
	private void setSelectedTagThumb(Thumb thumb) {
		selectedTagThumb.selected = false;
		selectedTagThumb.label.setBorder(new DashBorder(Color.BLACK, selectedTagThumb, false));
		thumb.selected = true;
		selectedTagThumb = thumb;
	}

	/**
	 * Selects a tag thumbnail. Called when the keypad arrows are pressed.
	 * Deselects the previously selected tag thumbnail and updates the thumbnail borders.
	 *
	 * @param disp the thumbnail displacement. ({@code KeyEvent.VK_UP}, {@code KeyEvent.VK_RIGHT},
	 * {@code KeyEvent.VK_DOWN} or {@code KeyEvent.VK_LEFT})
	 */
	private void setSelectedTagThumb(int disp) {
		if (selectedTagThumb != null) {
			int index = tagThumbnails.indexOf(selectedTagThumb) + disp;
			if (index >= 0 && index < tagThumbnails.size()) {
				Thumb thumb  = tagThumbnails.get(index);
				selectedTagThumb.selected = false;
				selectedTagThumb.label.setBorder(new DashBorder(Color.BLACK, selectedTagThumb, false));
				thumb.selected = true;
				selectedTagThumb = thumb;
				thumb.label.requestFocusInWindow();
				paintBorder(thumb, true);
				showImagePreview();
				upperScrollPanel.getVerticalScrollBar().setValue(((int) Math.ceil((index + 1) / 7.0) * 65) -
						upperScrollPanel.getVerticalScrollBar().getHeight());
			}
		}
	}

	/**
	 * Selects an image thumbnail, deselecting the previously selected image thumbnail and
	 * updating the thumbnail borders.
	 *
	 * @param thumb the {@code Thumb} to select
	 */
	public void setSelectedImageThumb(Thumb thumb) {
		selectedImageThumb.selected = false;
		selectedImageThumb.label.setBorder(new DashBorder(Color.BLACK, selectedImageThumb, false));
		thumb.selected = true;
		selectedImageThumb = thumb;
	}

	/**
	 * Selects an image thumbnail. Called when the keypad arrows are pressed.
	 * Deselects the previously selected image thumbnail and updates the thumbnail borders.
	 *
	 * @param disp the thumbnail displacement. ({@code KeyEvent.VK_UP}, {@code KeyEvent.VK_RIGHT},
	 * {@code KeyEvent.VK_DOWN} or {@code KeyEvent.VK_LEFT})
	 */
	public void setSelectedImageThumb(int disp) {
		if (selectedImageThumb != null) {
			int index = imgThumbnails.indexOf(selectedImageThumb) + disp;
			if (index >= 0 && index < imgThumbnails.size()) {
				Thumb thumb  = imgThumbnails.get(index);
				selectedImageThumb.selected = false;
				selectedImageThumb.label.setBorder(new DashBorder(Color.BLACK, selectedImageThumb, false));
				thumb.selected = true;
				selectedImageThumb = thumb;
				thumb.label.requestFocusInWindow();
				paintBorder(thumb, true);
				showImagePreview();
				lowerScrollPanel.getVerticalScrollBar().setValue(((int) Math.ceil((index + 1) / 5.0) * 80) -
						lowerScrollPanel.getVerticalScrollBar().getHeight());
			}
		}
	}

	//!
	public void paintBorder(Thumb thumb, boolean entered) {
		thumb.label.setBorder(new DashBorder(Color.BLACK, thumb, entered));
	}

	//! Draws the tag title background
	public void drawBackgrounds() {
		Graphics g = pTagTitle.getGraphics();
		BufferedImage tempImg = (BufferedImage)createImage(960, 46);
		Graphics tempG = tempImg.getGraphics();
		tempG.drawImage(new ImageIcon(getClass().getClassLoader().getResource("backgrounds/tagTitle.png")).getImage(), 0, 0, null);
		tempG.dispose();
		g.drawImage(tempImg, 0, 0, null);
		cachedTitleBackground = tempImg;
	}

	/**
     * Convenience method that returns a scaled instance of the
     * provided {@code BufferedImage}.
     *
     * @param img the original image to be scaled
     * @param targetWidth the desired width of the scaled instance,
     *    in pixels
     * @param targetHeight the desired height of the scaled instance,
     *    in pixels
     * @param higherQuality if true, this method will use a multi-step
     *    scaling technique that provides higher quality than the usual
     *    one-step technique (only useful in downscaling cases, where
     *    {@code targetWidth} or {@code targetHeight} is
     *    smaller than the original dimensions, and generally only when
     *    the {@code BILINEAR} hint is specified)
     * @return a scaled version of the original {@code BufferedImage}
     */
    @SuppressWarnings({"ConstantConditions"})
    public BufferedImage getScaledInstance(BufferedImage img, int targetWidth, int targetHeight,
                                           boolean higherQuality) {
        BufferedImage ret = img;
        int w, h;
        if (higherQuality && (img.getWidth() > targetWidth && img.getHeight() > targetHeight)) {
            // Use multi-step technique: start with original size, then
            // scale down in multiple passes with drawImage()
            // until the target size is reached
            w = img.getWidth();
            h = img.getHeight();
        } else {
            // Use one-step technique: scale directly from original
            // size to target size with a single drawImage() call
            w = targetWidth;
            h = targetHeight;
        }

        do {
            if (higherQuality && w > targetWidth) {
                w /= 2;
                if (w < targetWidth) {
                    w = targetWidth;
                }
            }

            if (higherQuality && h > targetHeight) {
                h /= 2;
                if (h < targetHeight) {
                    h = targetHeight;
                }
            }

            BufferedImage tmp = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2 = tmp.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2.drawImage(ret, 0, 0, w, h, null);
            g2.dispose();

            ret = tmp;
        } while (w != targetWidth || h != targetHeight);

        return ret;
    }

	@SuppressWarnings({"unchecked"})
	public void enableTag(boolean enable, String dateFile) {
		JSONObject joDate = null;
		if (new File(dateFile + ".date").exists()) {
			joDate = (JSONObject) JSONValue.parse(Utilities.loadString(dateFile + ".date"));
		}
		if (joDate == null) {
			joDate = new JSONObject();
		}
		joDate.put("enabled", enable);
		Utilities.saveString(dateFile + ".date", joDate.toJSONString());
//		String date = new File(dateFile + ".date").exists() ? Utilities.loadString(dateFile + ".date") : "";
	}

	static class PreviewPane extends JPanel implements PropertyChangeListener {
		private JLabel lblImgPreview;
		private int maxImgWidth;
		public PreviewPane() {
			setLayout(new BorderLayout(5, 5));
			setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
			add(new JLabel("Vista previa:"), BorderLayout.NORTH);
			lblImgPreview = new JLabel();
			lblImgPreview.setBackground(Color.WHITE);
			lblImgPreview.setOpaque(true);
			lblImgPreview.setPreferredSize(new Dimension(200, 200));
			maxImgWidth = 195;
			lblImgPreview.setBorder(BorderFactory.createEtchedBorder());
			add(lblImgPreview, BorderLayout.CENTER);
		}

		public void propertyChange(PropertyChangeEvent evt) {
			Icon icon = null;
			if(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
				File newFile = (File)evt.getNewValue();
				if(newFile != null) {
					String path = newFile.getAbsolutePath();
					if(/*path.endsWith(".gif") || */path.toLowerCase().endsWith(".jpg")/* || path.endsWith(".png") || path.endsWith(".bmp")*/) {
						try {
							BufferedImage img = ImageIO.read(newFile);
							float width = img.getWidth();
							float height = img.getHeight();
							float scale = height / width;
							width = maxImgWidth;
							height = (width * scale); // height should be scaled from new width
							icon = new ImageIcon(img.getScaledInstance(
									Math.max(1, (int)width), Math.max(1, (int)height), Image.SCALE_FAST));
						}
						catch(IOException e) {
							// couldn't read image.
						}
					}
				}

				lblImgPreview.setIcon(icon);
				this.repaint();
			}
		}
	}
}